﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Stauffware.Common.Core
{
	/// extensions for Reflection classes
	public static class Reflection_Extensions
	{
		/// <summary>
		/// Returns the one-and-only custom attribute of the given type, or null if not found.
		/// Throws an exception if there's more than one.
		/// </summary>
		/// <typeparam name="T">the type attribute to look for</typeparam>
		/// <param name="targetObj">the object whose custom attributes are to be searched</param>
		/// <param name="inherit">whether to search the type's inheritance chain</param>
		/// <returns>the one-and-only instance of the desired custom attribute, or null</returns>
		public static T GetCustomAttribute<T> (this ICustomAttributeProvider targetObj, bool inherit) where T : class
		{
			object[] attrs = targetObj.GetCustomAttributes(typeof(T), inherit);
			if (attrs == null || attrs.Length == 0)
				return null;
			if (attrs.Length == 1)
				return (T) attrs[0];
			throw new System.Reflection.AmbiguousMatchException(
							"found multiple attributes of type " + typeof(T).Name);
		}

		/// <summary>
		/// Returns whether the given type has the [Flags] attribute.
		/// This is intended for use with enum types.
		/// </summary>
		public static bool HasFlagsAttribute (this Type t)
		{
			return t.GetCustomAttribute<FlagsAttribute>(false) != null;
		}

		/// <summary>
		/// Returns true if the type is one of the primitive signed or unsigned
		/// integer types.
		/// </summary>
		public static bool IsPrimitiveInteger(this Type t)
		{
			return  t == typeof(int) || t == typeof(uint) ||
					t == typeof(byte) || t == typeof(sbyte) ||
					t == typeof(long) || t == typeof(ulong) ||
					t == typeof(ushort) || t == typeof(short);
		}

		/// <summary>
		/// Returns true if the type is one of the primitive scalar numeric types.
		/// </summary>
		public static bool IsPrimitiveNumber (this Type t)
		{
			return t.IsPrimitiveInteger() ||
					t == typeof(float) || t == typeof(double) || t == typeof(decimal);
		}

		/// <summary>
		/// Returns true if the type is Nullable&lt;&gt;.
		/// </summary>
		public static bool IsNullable (this Type t)
		{
			return (t != null) && t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(Nullable<>));		
		}

	}
}
